home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume91 / devices / apipe372 / part01 / pgmpipe.c < prev    next >
C/C++ Source or Header  |  1991-11-14  |  21KB  |  813 lines

  1. /*
  2. **  Pgmpipe.c --- main module of APipe-Handler
  3. **
  4. **  Copyright (C) 1991 by Per Bojsen.  All Rights Reserved.
  5. **
  6. **  Permission is granted to any individual or institution to use, copy,
  7. **  modify, and distribute this software, provided that this complete
  8. **  copyright and permission notice is maintained, intact, in all copies
  9. **  and supporting documentation.
  10. **
  11. **  This software is provided on an "as is" basis without express or
  12. **  implied warranty.
  13. **
  14. **  NOTE: The code is reentrant.  Be careful when modifying it.
  15. **
  16. **  $Id: pgmpipe.c,v 1.3 91/10/01 02:35:30 bojsen Exp Locker: bojsen $
  17. **
  18. **  $Log:    pgmpipe.c,v $
  19. **  Revision 1.3  91/10/01  02:35:30  bojsen
  20. **  Added code to clone current dir of requester process.  Changed code
  21. **  to obtain pointer to requester process.
  22. **
  23. **  Revision 1.2  91/09/24  23:59:44  bojsen
  24. **  Fixed typo in #include line.
  25. **
  26. **  Revision 1.1  91/09/24  23:54:06  bojsen
  27. **  Initial revision
  28. **
  29. */
  30.  
  31. #include <exec/types.h>
  32. #include <exec/memory.h>
  33. #include <utility/tagitem.h>
  34. #include <clib/macros.h>
  35. #include <dos/dos.h>
  36. #include <dos/dosextens.h>
  37. #include <dos/dostags.h>
  38. #include <dos/var.h>
  39. #ifdef __SASC
  40. #include <proto/exec.h>
  41. #include <proto/dos.h>
  42. #endif /* __SASC */
  43. #include <string.h>
  44. #include "APipe-Handler_rev.h"
  45.  
  46. /*
  47.  *  Version tagging.
  48.  */
  49. STATIC char VersionTag[] = VERSTAG;
  50.  
  51. /*
  52.  *  Debugging.
  53.  */
  54. #ifdef DEBUG
  55. #define DPrintF(args) do { KPrintF args; } while (0)
  56. #else /* !DEBUG */
  57. #define DPrintF(args)
  58. #endif /* !DEBUG */
  59.  
  60. #define AbsExecBase 4L
  61. #define LIB_VERSION_SUPPORTED 37L
  62. #define PIPEBUFFER_SIZE 4096
  63.  
  64. /* defines for read/write status */
  65. #define PPIPE_NOTOPEN    0
  66. #define PPIPE_OPEN    4
  67. #define PPIPE_READ    1
  68. #define PPIPE_READOPEN    (PGMPIPE_READ | PGMPIPE_OPEN)
  69. #define PPIPE_WRITE    2
  70. #define PPIPE_WRITEOPEN (PGMPIPE_WRITE | PGMPIPE_OPEN)
  71. #define PPIPE_RWMASK    3
  72.  
  73. /* defines for fh_Arg1 field of filehandles */
  74. #define PPIPE_READER  1
  75. #define PPIPE_WRITER  2
  76.  
  77. /* defines for closing variable */
  78. #define PPIPE_C_OPEN          0
  79. #define PPIPE_C_RDRCLOSING    1
  80. #define PPIPE_C_WRTCLOSING    2
  81. #define PPIPE_C_CLOSINGMASK   3
  82. #define PPIPE_C_RDRCLOSED     4
  83. #define PPIPE_C_WRTCLOSED     8
  84. #define PPIPE_C_CLOSEDMASK  0xC
  85.  
  86. /*
  87.  *  Parameter packet to child process.
  88.  */
  89. struct ChildMsg
  90. {
  91.   struct Message  ExecMsg;
  92.   char         *CmdLine;   /* the command line to execute */
  93.   LONG          StackSize; /* use this stacksize for child's child */
  94.   BPTR          PathList;  /* path list for child */
  95.   ULONG       Flags;     /* flags for child */
  96.   LONG          RC;         /* return code from child process */
  97. };
  98.  
  99. /* Defines for ChildMsg.Flags */
  100. #define CHMF_PIPE 1L /* stdin of child is a pipe (of PIPE: type) */
  101. #define CHMF_PATH 2L /* path is passed in in PathList */
  102.  
  103. /*
  104.  *  Command path element.
  105.  */
  106. struct PathEntry
  107. {
  108.   BPTR pe_NextPathEntry;
  109.   BPTR pe_PathLock;
  110. };
  111.  
  112. /*
  113.  *  The library bases must be global by AmigaOS programming standards,
  114.  *  and the pragma libcall requires them.
  115.  */
  116. struct ExecBase *SysBase;
  117. struct DosLibrary *DOSBase;
  118.  
  119.  
  120. /*
  121.  *  Prototypes for external functions.
  122.  */
  123. extern int _ChildProcess(void);
  124.  
  125. /*
  126.  *  Prototypes for local functions.
  127.  */
  128. ULONG CopyFromFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE *End,
  129.            ULONG *FIFOFill, UBYTE *Dest, ULONG NumBytes);
  130. ULONG CopyToFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE **End,
  131.          ULONG *FIFOFill, UBYTE *Src, ULONG NumBytes);
  132. LONG ExecCommand(char *CmdLine, struct MsgPort **ParentPort,
  133.          struct Process *Requester, struct FileHandle *ChildIO,
  134.          LONG IoDirection);
  135. BPTR CloneProcessIO(struct Process *Friend, LONG IoDirection);
  136. BPTR ClonePathList(struct CommandLineInterface *Peer);
  137. void FreePathList(BPTR);
  138.  
  139. void
  140. PgmPipe()
  141. {
  142.   struct DosLibrary *l_DOSBase;
  143.  
  144.   SysBase = *((struct ExecBase **) AbsExecBase); /* `open' exec.library */
  145.  
  146.   if (l_DOSBase =
  147.     (struct DosLibrary *) OpenLibrary("dos.library", LIB_VERSION_SUPPORTED))
  148.   {
  149.     struct Process *ourProc, *theirProc;
  150.     struct DosPacket *packet, *writePkt = NULL, *readPkt = NULL;
  151.     struct DosPacket *writeCls = NULL, *readCls = NULL;
  152.     struct MsgPort *childPort = NULL;
  153.     struct MsgPort *oldConTask = NULL;
  154.     struct ChildMsg *childMsg = NULL;
  155.     struct FileHandle *fhIncoming, *fhOutgoing;
  156.     UBYTE *pipeBuffer;
  157.     UBYTE *pbStart, *pbEnd;
  158.     ULONG bytesReady, bytesR = 0, bytesW = 0;
  159.     UWORD closing = PPIPE_C_RDRCLOSED | PPIPE_C_WRTCLOSED;
  160.     UWORD readwrite = PPIPE_NOTOPEN;
  161.     ULONG waitSigMask;
  162.     struct MinList readQ, writeQ;
  163. #ifdef DEBUG
  164.     ULONG packetNum = 0;
  165. #endif /* DEBUG */
  166.  
  167.     NewList((struct List *) &readQ);
  168.     NewList((struct List *) &writeQ);
  169.  
  170.     DOSBase = l_DOSBase; /* patch the global library base */
  171.  
  172.     ourProc = (struct Process *) FindTask(NULL);
  173.     waitSigMask = 1L << ourProc->pr_MsgPort.mp_SigBit;
  174.     packet = WaitPkt(); /* get parameter packet */
  175.  
  176.     if ((pipeBuffer = AllocMem(PIPEBUFFER_SIZE, 0)) == NULL)
  177.       ReplyPkt(packet, DOSFALSE, ERROR_NO_FREE_STORE);
  178.     else
  179.     {
  180.       pbStart = pbEnd = pipeBuffer;
  181.       bytesReady = 0;
  182.  
  183.       /*
  184.        *  Currently we don't use the parameters provided in the parameter
  185.        *  packet.  Since we want a new process for each instance of reference
  186.        *  to our handler we don't patch the DeviceList node.
  187.        */
  188.  
  189.       ReplyPkt(packet, DOSTRUE, packet->dp_Arg2);
  190.  
  191.       do
  192.       {
  193.     struct Node *node;
  194.     char *cmdLine;
  195.     LONG ioerr;
  196.  
  197.     if (readPkt && bytesReady == 0 && writePkt == NULL &&
  198.         closing & PPIPE_C_WRTCLOSED)
  199.     {
  200.       ReplyPkt(readPkt, bytesR, 0);
  201.       readPkt = NULL;
  202.     }
  203.  
  204. #ifdef DEBUG
  205.     if (readPkt || writePkt)
  206.       DPrintF(("Event loop: readPkt == 0x%08lx writePkt == 0x%08lx\n",
  207.            readPkt, writePkt));
  208. #endif /* DEBUG */
  209.  
  210.     if (packet = WaitPkt())
  211.     {
  212.       DPrintF(("Packet %ld, port 0x%08lx: ", ++packetNum, packet->dp_Port));
  213.  
  214.       switch (packet->dp_Type)
  215.       {
  216.         struct TagItem adoTags[2];
  217.  
  218.         case ACTION_FINDINPUT:
  219.           DPrintF(("ACTION_FINDINPUT 0x%08lx 0x%08lx %s\n",
  220.                packet->dp_Arg1 << 2, packet->dp_Arg2 << 2,
  221.                (char *) (packet->dp_Arg3 << 2) + 1));
  222.  
  223.           if (readwrite != PPIPE_NOTOPEN)
  224.           {
  225.         ReplyPkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE);
  226.         break;
  227.           }
  228.           else
  229.         readwrite = PPIPE_READ;
  230.  
  231.         case ACTION_FINDOUTPUT:
  232. #ifdef DEBUG
  233.           if (packet->dp_Type == ACTION_FINDOUTPUT)
  234.         DPrintF(("ACTION_FINDOUTPUT 0x%08lx 0x%08lx %s\n",
  235.              packet->dp_Arg1 << 2, packet->dp_Arg2 << 2,
  236.              (char *) (packet->dp_Arg3 << 2) + 1));
  237. #endif /* DEBUG */
  238.  
  239.           if (readwrite & PPIPE_OPEN)
  240.           {
  241.         ReplyPkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE);
  242.         break;
  243.           }
  244.           else if (readwrite != PPIPE_READ)
  245.         readwrite = PPIPE_WRITE;
  246.  
  247.           fhIncoming = (struct FileHandle *) BADDR(packet->dp_Arg1);
  248.  
  249.           /*
  250.            *  Obtain command line to execute.
  251.            */
  252.           cmdLine = (char *) BADDR(packet->dp_Arg3) + 1;
  253.           while (*cmdLine && *cmdLine != ':')
  254.         cmdLine++;
  255.           if (*cmdLine == ':')
  256.         cmdLine++;
  257.  
  258.           /*
  259.            *  Find out who sent the packet.
  260.            */
  261.           if ((packet->dp_Port->mp_Flags & PF_ACTION) == PA_SIGNAL)
  262.         theirProc = (struct Process *) packet->dp_Port->mp_SigTask;
  263.           else
  264.           {
  265.         ReplyPkt(packet, DOSFALSE, ERROR_BAD_STREAM_NAME);
  266.         readwrite = PPIPE_NOTOPEN;
  267.         break;
  268.           }
  269.  
  270.           adoTags[0].ti_Tag = ADO_FH_Mode;
  271.           adoTags[0].ti_Data =
  272.         readwrite == PPIPE_READ ? MODE_NEWFILE : MODE_OLDFILE;
  273.           adoTags[1].ti_Tag = TAG_DONE;
  274.  
  275.           if ((fhOutgoing = AllocDosObject(DOS_FILEHANDLE, adoTags))
  276.             == NULL)
  277.           {
  278.         ReplyPkt(packet, DOSFALSE, ERROR_NO_FREE_STORE);
  279.         readwrite = PPIPE_NOTOPEN;
  280.         break;
  281.           }
  282.  
  283.           fhOutgoing->fh_Link = NULL;
  284.           fhOutgoing->fh_Port = fhIncoming->fh_Port = (struct MsgPort *) 0;
  285.           fhOutgoing->fh_Type = &ourProc->pr_MsgPort;
  286.  
  287.           oldConTask = SetConsoleTask(theirProc->pr_ConsoleTask);
  288.  
  289.           DPrintF(("Getting ready to spawn child . . .  "));
  290.  
  291.           if (ioerr = ExecCommand(cmdLine, &childPort, theirProc,
  292.                       fhOutgoing, readwrite))
  293.           {
  294.         SetConsoleTask(oldConTask);
  295.         ReplyPkt(packet, DOSFALSE, ioerr);
  296.         FreeDosObject(DOS_FILEHANDLE, fhOutgoing);
  297.         readwrite = PPIPE_NOTOPEN;
  298.         break;
  299.           }
  300.           SetConsoleTask(oldConTask);
  301.  
  302.           DPrintF(("Child spawned.\n"));
  303.  
  304.           waitSigMask |= 1L << childPort->mp_SigBit;
  305.  
  306.           if (readwrite == PPIPE_READ)
  307.           {
  308.         fhIncoming->fh_Arg1 = PPIPE_READER;
  309.         fhOutgoing->fh_Arg1 = PPIPE_WRITER;
  310.           }
  311.           else
  312.           {
  313.         fhIncoming->fh_Arg1 = PPIPE_WRITER;
  314.         fhOutgoing->fh_Arg1 = PPIPE_READER;
  315.           }
  316.  
  317.           closing = PPIPE_C_OPEN;
  318.           readwrite |= PPIPE_OPEN;
  319.  
  320.           ReplyPkt(packet, DOSTRUE, packet->dp_Res2);
  321.  
  322.           break;
  323.  
  324.         case ACTION_READ:
  325.           DPrintF(("ACTION_READ %ld 0x%08lx %ld\n", packet->dp_Arg1,
  326.                packet->dp_Arg2, packet->dp_Arg3));
  327.  
  328.           if (packet->dp_Arg2 == NULL || packet->dp_Arg3 < 0)
  329.         ReplyPkt(packet, DOSFALSE, ERROR_BAD_NUMBER);
  330.           else if (closing & PPIPE_C_RDRCLOSED)
  331.         ReplyPkt(packet, DOSFALSE, ERROR_INVALID_LOCK);
  332.           else if (packet->dp_Arg1 == PPIPE_WRITER)
  333.         ReplyPkt(packet, 0, 0);
  334.           else if (readPkt == NULL)
  335.           {
  336.         readPkt = packet;
  337.         bytesR = 0;
  338.           }
  339.           else
  340.         AddTail((struct List *) &readQ,
  341.             (struct Node *) packet->dp_Link);
  342.           break;
  343.  
  344.         case ACTION_WRITE:
  345.           DPrintF(("ACTION_WRITE %ld 0x%08lx %ld\n", packet->dp_Arg1,
  346.                packet->dp_Arg2, packet->dp_Arg3));
  347.  
  348.           if (packet->dp_Arg2 == NULL || packet->dp_Arg3 < 0)
  349.         ReplyPkt(packet, DOSFALSE, ERROR_BAD_NUMBER);
  350.           else if (closing & PPIPE_C_WRTCLOSED)
  351.         ReplyPkt(packet, DOSFALSE, ERROR_INVALID_LOCK);
  352.           else if (packet->dp_Arg1 == PPIPE_READER)
  353.         ReplyPkt(packet, 0, 0);
  354.           else if (writePkt == NULL)
  355.           {
  356.         writePkt = packet;
  357.         bytesW = 0;
  358.           }
  359.           else
  360.         AddTail((struct List *) &writeQ,
  361.             (struct Node *) packet->dp_Link);
  362.           break;
  363.  
  364.         case ACTION_END:
  365.           DPrintF(("ACTION_END %ld\n", packet->dp_Arg1));
  366.  
  367.           if (packet->dp_Arg1 == PPIPE_READER &&
  368.           (closing & PPIPE_C_RDRCLOSED) == 0)
  369.           {
  370.         closing |= PPIPE_C_RDRCLOSING;
  371.         readCls = packet;
  372.           }
  373.           else if (packet->dp_Arg1 == PPIPE_WRITER &&
  374.                (closing & PPIPE_C_WRTCLOSED) == 0)
  375.           {
  376.         closing |= PPIPE_C_WRTCLOSING;
  377.         writeCls = packet;
  378.           }
  379.           else
  380.         ReplyPkt(packet, DOSTRUE, 0);
  381.           break;
  382.  
  383.         case ACTION_IS_FILESYSTEM:
  384.           DPrintF(("ACTION_IS_FILESYSTEM\n"));
  385.  
  386.           ReplyPkt(packet, DOSFALSE, 0);
  387.           break;
  388.  
  389.         default:
  390.           DPrintF(("%ld\n", packet->dp_Type));
  391.  
  392.           ReplyPkt(packet, DOSFALSE, ERROR_ACTION_NOT_KNOWN);
  393.       }
  394.  
  395.       DPrintF(("\n"));
  396.     }
  397.  
  398.     do
  399.     {
  400.       if (readPkt)
  401.       {
  402.         if (bytesReady > 0)
  403.           bytesR += CopyFromFIFO(pipeBuffer, PIPEBUFFER_SIZE, &pbStart,
  404.                      pbEnd, &bytesReady,
  405.                      (UBYTE *) readPkt->dp_Arg2 + bytesR,
  406.                      readPkt->dp_Arg3 - bytesR);
  407.         if (bytesR < readPkt->dp_Arg3 && writePkt)
  408.         {
  409.           ULONG bytes = MIN(writePkt->dp_Arg3 - bytesW,
  410.                 readPkt->dp_Arg3 - bytesR);
  411.  
  412.           CopyMem((UBYTE *) writePkt->dp_Arg2 + bytesW,
  413.               (UBYTE *) readPkt->dp_Arg2 + bytesR, bytes);
  414.           bytesR += bytes;
  415.           if ((bytesW += bytes) == writePkt->dp_Arg3)
  416.           {
  417.         ReplyPkt(writePkt, writePkt->dp_Arg3, 0);
  418.         writePkt = NULL;
  419.           }
  420.         }
  421.         if (bytesR == readPkt->dp_Arg3)
  422.         {
  423.           ReplyPkt(readPkt, readPkt->dp_Arg3, 0);
  424.           readPkt = NULL;
  425.         }
  426.       }
  427.       if (readPkt == NULL &&
  428.           (readPkt = (struct DosPacket *)
  429.                (node = RemHead((struct List *) &readQ),
  430.                 node ? node->ln_Name : NULL)))
  431.       {
  432.         bytesR = 0;
  433.       }
  434.       if (writePkt == NULL &&
  435.           (writePkt = (struct DosPacket *)
  436.                 (node = RemHead((struct List *) &writeQ),
  437.                  node ? node->ln_Name : NULL)))
  438.       {
  439.         bytesW = 0;
  440.       }
  441.     }
  442.     while (readPkt && (writePkt || bytesReady > 0));
  443.  
  444.     if (readPkt == NULL && closing & PPIPE_C_RDRCLOSING)
  445.     {
  446.       closing &= ~PPIPE_C_RDRCLOSING;
  447.       closing |= PPIPE_C_RDRCLOSED;
  448.     }
  449.  
  450.     while (writePkt &&
  451.            (closing & PPIPE_C_RDRCLOSED ||
  452.         PIPEBUFFER_SIZE - bytesReady >= writePkt->dp_Arg3 - bytesW))
  453.     {
  454.       if (closing & PPIPE_C_RDRCLOSED)
  455.       {
  456.         ReplyPkt(writePkt, writePkt->dp_Arg3, 0);
  457.         writePkt = NULL;
  458.       }
  459.       else
  460.       {
  461.         ULONG bytes = CopyToFIFO(pipeBuffer, PIPEBUFFER_SIZE, &pbStart,
  462.                      &pbEnd, &bytesReady,
  463.                      (UBYTE *) writePkt->dp_Arg2 + bytesW,
  464.                      writePkt->dp_Arg3 - bytesW);
  465.  
  466.         if ((bytesW += bytes) == writePkt->dp_Arg3)
  467.         {
  468.           ReplyPkt(writePkt, writePkt->dp_Arg3, 0);
  469.           writePkt = NULL;
  470.         }
  471.       }
  472.       if (writePkt == NULL &&
  473.           (writePkt = (struct DosPacket *)
  474.                 (node = RemHead((struct List *) &writeQ),
  475.                  node ? node->ln_Name : NULL)))
  476.       {
  477.         bytesW = 0;
  478.       }
  479.     }
  480.  
  481.     if (writePkt == NULL && closing & PPIPE_C_WRTCLOSING)
  482.     {
  483.       closing &= ~PPIPE_C_WRTCLOSING;
  484.       closing |= PPIPE_C_WRTCLOSED;
  485.     }
  486.       }
  487.       while ((closing & PPIPE_C_RDRCLOSED) == 0 ||
  488.          (closing & PPIPE_C_WRTCLOSED) == 0);
  489.  
  490.       /* clean up from child process */
  491.       if (childPort)
  492.       {
  493.     waitSigMask = 1L << childPort->mp_SigBit;
  494.  
  495.     while ((childMsg = (struct ChildMsg *) GetMsg(childPort)) == NULL)
  496.       Wait(waitSigMask);
  497.  
  498.     DeleteMsgPort(childPort);
  499.       }
  500.       if (childMsg) /* means the child process existed */
  501.       {
  502.     if (readCls)
  503.       ReplyPkt(readCls, DOSTRUE, 0);
  504.     if (writeCls)
  505.       ReplyPkt(writeCls, DOSTRUE, 0);
  506.     FreePathList(childMsg->PathList);
  507.     FreeVec(childMsg); /* no use for return code currently */
  508.       }
  509.       FreeMem(pipeBuffer, PIPEBUFFER_SIZE);
  510.     }
  511.  
  512.     CloseLibrary((struct Library *) l_DOSBase);
  513.  
  514.     DPrintF(("Handler exiting.\n"));
  515.   }
  516. } /* PgmPipe */
  517.  
  518.  
  519. STATIC ULONG
  520. CopyFromFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE *End,
  521.          ULONG *FIFOFill, UBYTE *Dest, ULONG NumBytes)
  522. {
  523.   ULONG bytesCopied = 0, bytes;
  524.  
  525.   DPrintF(("CopyFromFIFO: 0x%08lx %ld", Dest, NumBytes));
  526.  
  527.   if (*FIFOFill > 0 && NumBytes > 0)
  528.   {
  529.     bytes = *Start >= End ? FIFO + FIFOSize - *Start : *FIFOFill;
  530.  
  531.     CopyMem(*Start, Dest, bytesCopied = MIN(bytes, NumBytes));
  532.     NumBytes -= bytesCopied;
  533.     *Start += bytesCopied;
  534.     *FIFOFill -= bytesCopied;
  535.   }
  536.   if (*FIFOFill > 0 && NumBytes > 0)
  537.   {
  538.     bytes = End - FIFO; /* invariant: *Start == FIFO + FIFOSize */
  539.  
  540.     CopyMem(FIFO, Dest + bytesCopied, bytes = MIN(bytes, NumBytes));
  541.     *Start = FIFO + bytes;
  542.     *FIFOFill -= bytes;
  543.     bytesCopied += bytes;
  544.   }
  545.  
  546.   DPrintF((" %ld\n\n", bytesCopied));
  547.  
  548.   return bytesCopied;
  549. } /* CopyFromFIFO */
  550.  
  551.  
  552. STATIC ULONG
  553. CopyToFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE **End,
  554.        ULONG *FIFOFill, UBYTE *Src, ULONG NumBytes)
  555. {
  556.   ULONG bytesCopied = 0, bytes;
  557.  
  558.   DPrintF(("CopyToFIFO: 0x%08lx %ld", Src, NumBytes));
  559.  
  560.   if (*FIFOFill == 0)
  561.     *Start = *End = FIFO; /* normalize for efficiency */
  562.  
  563.   if (*FIFOFill < FIFOSize && NumBytes > 0)
  564.   {
  565.     bytes = *End <= *Start ? FIFOSize - *FIFOFill : FIFO + FIFOSize - *End;
  566.  
  567.     CopyMem(Src, *End, bytesCopied = MIN(bytes, NumBytes));
  568.     NumBytes -= bytesCopied;
  569.     *End += bytesCopied;
  570.     *FIFOFill += bytesCopied;
  571.   }
  572.   if (*FIFOFill < FIFOSize && NumBytes > 0)
  573.   {
  574.     bytes = *Start - FIFO; /* invariant: *End == FIFO + FIFOSize */
  575.  
  576.     CopyMem(Src + bytesCopied, FIFO, bytes = MIN(bytes, NumBytes));
  577.     *End = FIFO + bytes;
  578.     *FIFOFill += bytes;
  579.     bytesCopied += bytes;
  580.   }
  581.  
  582.   DPrintF((" %ld\n\n", bytesCopied));
  583.  
  584.   return bytesCopied;
  585. } /* CopyFromFIFO */
  586.  
  587.  
  588. #define FAILATCMD     "FailAt 2147483647\n"
  589. #define STRLEN_FAILATCMD 18
  590.  
  591. /*
  592.  *  Run a command asynchronously.
  593.  */
  594.  
  595. STATIC LONG
  596. ExecCommand(char *CmdLine, struct MsgPort **ParentPort,
  597.         struct Process *Requester, struct FileHandle *ChildIO,
  598.         LONG IoDirection)
  599. {
  600.   char *commandLine;
  601.   struct ChildMsg *childMsg;
  602.   struct TagItem processTags[9];
  603.   struct Process *child, *thisProcess = (struct Process *) FindTask(NULL);
  604.   struct CommandLineInterface *cli = BADDR(Requester->pr_CLI);
  605.   BPTR childOI;
  606.   struct LocalVar *pVar;
  607.   BPTR pathList, homeDir;
  608.   LONG ioErr;
  609.  
  610.   /*
  611.    *  Copy shell variables, aliases, and path from requester process.
  612.    *  NOTE: This is not completely safe!
  613.    */
  614.   Forbid();
  615.   for (pVar = (struct LocalVar *) Requester->pr_LocalVars.mlh_TailPred;
  616.        pVar->lv_Node.ln_Pred;
  617.        pVar = (struct LocalVar *) pVar->lv_Node.ln_Pred)
  618.   {
  619.     if (FindVar(pVar->lv_Node.ln_Name, pVar->lv_Node.ln_Type) == NULL)
  620.       SetVar(pVar->lv_Node.ln_Name, pVar->lv_Value, pVar->lv_Len,
  621.          pVar->lv_Flags | pVar->lv_Node.ln_Type & ~LVF_IGNORE);
  622.   }
  623.  
  624.   pathList = ClonePathList(cli);
  625.  
  626.   Permit();
  627.  
  628.   if ((childOI = CloneProcessIO(Requester, IoDirection)) == NULL)
  629.   {
  630.     ioErr = IoErr();
  631.     FreePathList(pathList);
  632.  
  633.     return ioErr;
  634.   }
  635.  
  636.   if ((homeDir = DupLock(Requester->pr_CurrentDir)) == NULL)
  637.   {
  638.     ioErr = IoErr();
  639.     Close(childOI);
  640.     FreePathList(pathList);
  641.  
  642.     return ioErr;
  643.   }
  644.  
  645.   /*
  646.    *  Create parent's port.
  647.    */
  648.   if ((*ParentPort = CreateMsgPort()) == NULL)
  649.   {
  650.     UnLock(homeDir);
  651.     Close(childOI);
  652.     FreePathList(pathList);
  653.  
  654.     return ERROR_NO_FREE_STORE;
  655.   }
  656.  
  657.   /*
  658.    *  Build command line from argument vector.    First count the length.
  659.    *
  660.    *  We insert a `FailAt 2147483647' (0x7fffffff) so the shell doesn't
  661.    *  print `Command foo failed' when we call System() to execute the
  662.    *  command.
  663.    */
  664.   if ((childMsg = AllocVec(sizeof(struct ChildMsg) + strlen(CmdLine) +
  665.                STRLEN_FAILATCMD + 1, MEMF_PUBLIC)) == NULL)
  666.   {
  667.     DeleteMsgPort(*ParentPort);
  668.     *ParentPort = NULL;
  669.     UnLock(homeDir);
  670.     Close(childOI);
  671.     FreePathList(pathList);
  672.  
  673.     return ERROR_NO_FREE_STORE;
  674.   }
  675.  
  676.   childMsg->ExecMsg.mn_Node.ln_Type = NT_MESSAGE;
  677.   childMsg->ExecMsg.mn_Node.ln_Pri = 0;
  678.   childMsg->ExecMsg.mn_ReplyPort = *ParentPort;
  679.   childMsg->ExecMsg.mn_Length = sizeof(struct ChildMsg);
  680.   childMsg->CmdLine = commandLine = (char *) (childMsg + 1);
  681.  
  682.   /* Setup the grandchild's [sic] stack */
  683.   childMsg->StackSize = cli ? cli->cli_DefaultStack << 2 :
  684.                   Requester->pr_StackSize;
  685.   childMsg->PathList = pathList;
  686.   childMsg->Flags = pathList ? CHMF_PATH : 0;
  687.   childMsg->RC = 0;
  688.  
  689.   strcpy(commandLine, FAILATCMD);
  690.   strcpy(commandLine + STRLEN_FAILATCMD, CmdLine);
  691.  
  692.   processTags[0].ti_Tag = NP_Entry;
  693.   processTags[0].ti_Data = (Tag) _ChildProcess;
  694.   processTags[1].ti_Tag = NP_Input;
  695.   processTags[2].ti_Tag = NP_Output;
  696.   processTags[3].ti_Tag = NP_StackSize;
  697.   processTags[3].ti_Data = thisProcess->pr_StackSize;
  698.   processTags[4].ti_Tag = NP_Cli;
  699.   processTags[4].ti_Data = TRUE;
  700.   processTags[5].ti_Tag = NP_Name;
  701.   processTags[5].ti_Data = (Tag) "Kicker Process";
  702.   processTags[6].ti_Tag = NP_Priority;
  703.   processTags[6].ti_Data = Requester->pr_Task.tc_Node.ln_Pri;
  704.   processTags[7].ti_Tag = NP_CurrentDir;
  705.   processTags[7].ti_Data = homeDir;
  706.   processTags[8].ti_Tag = TAG_DONE;
  707.  
  708.   if (IoDirection == PPIPE_READ)
  709.   {
  710.     processTags[1].ti_Data = childOI;
  711.     processTags[2].ti_Data = MKBADDR(ChildIO);
  712.   }
  713.   else /* IoDirection == PPIPE_WRITE */
  714.   {
  715.     processTags[1].ti_Data = MKBADDR(ChildIO);
  716.     processTags[2].ti_Data = childOI;
  717.   }
  718.  
  719.   /*
  720.    *  Start our `kicker' process.  This process consists of the _ChildProcess()
  721.    *  function above.  The _ChildProcess() function then executes the command.
  722.    */
  723.   if ((child = CreateNewProc(processTags)) == NULL)
  724.   {
  725.     FreeVec(childMsg);
  726.     DeleteMsgPort(*ParentPort);
  727.     *ParentPort = NULL;
  728.     UnLock(homeDir);
  729.     Close(childOI);
  730.     FreePathList(pathList);
  731.  
  732.     return ERROR_NO_FREE_STORE;
  733.   }
  734.  
  735.   /* now pass the child the startup message */
  736.   PutMsg(&child->pr_MsgPort, (struct Message *) childMsg);
  737.  
  738.   return 0;
  739. } /* ExecCommand */
  740.  
  741.  
  742. STATIC BPTR
  743. CloneProcessIO(struct Process *Friend, LONG IoDirection)
  744. {
  745.   BPTR origFH;
  746.   BPTR lock;
  747.   BPTR fh;
  748.  
  749.   if (IoDirection == PPIPE_READ)
  750.     origFH = Friend->pr_CIS;
  751.   else
  752.     origFH = Friend->pr_COS;
  753.  
  754.   if ((lock = DupLockFromFH(origFH)) == NULL ||
  755.       (fh = OpenFromLock(lock)) == NULL)
  756.   {
  757.     if (lock)
  758.       UnLock(lock);
  759.     fh = Open("CONSOLE:", MODE_OLDFILE);
  760.   }
  761.  
  762.   return fh;
  763. } /* CloneProcessIO */
  764.  
  765.  
  766. /*
  767.  *  NOTE: This routine should be called from within a Forbid();
  768.  *      but it is still unsafe due to the call to DupLock().
  769.  */
  770. STATIC BPTR
  771. ClonePathList(struct CommandLineInterface *Peer)
  772. {
  773.   BPTR pathList = NULL;
  774.   struct PathEntry *pathEntry, *pathET;
  775.  
  776.   if (Peer)
  777.   {
  778.     for (pathEntry = BADDR(Peer->cli_CommandDir),
  779.      pathET = (struct PathEntry *) &pathList;
  780.      pathEntry;
  781.      pathEntry = BADDR(pathEntry->pe_NextPathEntry))
  782.     {
  783.       struct PathEntry *newPE;
  784.  
  785.       if (newPE = AllocMem(sizeof(struct PathEntry), MEMF_PUBLIC))
  786.       {
  787.     newPE->pe_NextPathEntry = NULL;
  788.     newPE->pe_PathLock = DupLock(pathEntry->pe_PathLock);
  789.     pathET->pe_NextPathEntry = MKBADDR(newPE);
  790.     pathET = newPE;
  791.       }
  792.       else
  793.     break;
  794.     }
  795.   }
  796.  
  797.   return pathList;
  798. } /* ClonePathList */
  799.  
  800.  
  801. STATIC void
  802. FreePathList(BPTR PathList)
  803. {
  804.   struct PathEntry *pathEntry;
  805.  
  806.   while (pathEntry = BADDR(PathList))
  807.   {
  808.     PathList = pathEntry->pe_NextPathEntry;
  809.     UnLock(pathEntry->pe_PathLock);
  810.     FreeMem(pathEntry, sizeof(struct PathEntry));
  811.   }
  812. } /* FreePathList */
  813.